package com.example.supplychainbackend.service.Impl;

import com.example.supplychainbackend.controller.dto.blockchain.UploadInfo;
import com.example.supplychainbackend.controller.dto.enterprise.CertApplyProgressDto;
import com.example.supplychainbackend.controller.dto.enterprise.EnterpriseCertDto;
import com.example.supplychainbackend.controller.dto.enterprise.EnterpriseDto;
import com.example.supplychainbackend.dao.EnterpriseCertDao;
import com.example.supplychainbackend.dao.EnterpriseDao;
import com.example.supplychainbackend.entity.EnterpriseCertEntity;
import com.example.supplychainbackend.entity.EnterpriseCertStageEntity;
import com.example.supplychainbackend.entity.EnterpriseEntity;
import com.example.supplychainbackend.enums.enterprise.CertStage;
import com.example.supplychainbackend.enums.enterprise.ReviewStage;
import com.example.supplychainbackend.mapper.EnterpriseMapper;
import com.example.supplychainbackend.service.BlockchainService;
import com.example.supplychainbackend.service.EnterpriseService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class EnterpriseServiceImpl implements EnterpriseService {

    private final EnterpriseDao enterpriseDao;
    private final EnterpriseCertDao certDao;
    private final EnterpriseMapper mapper;

    private final BlockchainService blockchainService;

    public Long createEnterprise(EnterpriseDto enterpriseDto) {
        EnterpriseEntity entity = mapper.toInfoEntity(enterpriseDto);
        EnterpriseEntity entityS =enterpriseDao.save(entity);

        List<EnterpriseCertStageEntity> certStageEntities = new ArrayList<>();
        certStageEntities.add(EnterpriseCertStageEntity.builder().message(CertStage.APPLY.getDesc()).status(ReviewStage.NOT.getDesc())
                .time("").build());
        certStageEntities.add(EnterpriseCertStageEntity.builder().message(CertStage.PRE_REVIEW.getDesc()).status(ReviewStage.NOT.getDesc())
                .time("").build());
        certStageEntities.add(EnterpriseCertStageEntity.builder().message(CertStage.REVIEW.getDesc()).status(ReviewStage.NOT.getDesc())
                .time("").build());
        certStageEntities.add(EnterpriseCertStageEntity.builder().message(CertStage.FINISHED.getDesc()).status(ReviewStage.NOT.getDesc())
                .time("").build());

        EnterpriseCertEntity certEntity = EnterpriseCertEntity.builder()
                .name(entityS.getCompany())
                .eid(entityS.getId())
                .code(entityS.getCode()).build();
        certEntity.setEid(entityS.getId());
        certEntity.setStatus(1);
        certEntity.setOnChain(false);

        certEntity.setProgress(certStageEntities);
        certDao.save(certEntity);
        return entityS.getId();
    }

    public EnterpriseDto updateEnterprise(Long id, EnterpriseDto enterpriseDto) {
        EnterpriseEntity entity = mapper.toInfoEntity(enterpriseDto);
        entity.setId(id);
        EnterpriseEntity entityS =enterpriseDao.save(entity);
        return mapper.toInfoDto(entityS);
    }

    @Override
    public List<EnterpriseDto> listEnterprises() {
        List<EnterpriseEntity> res = enterpriseDao.findAll();
        return mapper.toDtoList(res);
    }

    public EnterpriseDto getEnterprise(Long id) {
        Optional<EnterpriseEntity> res =enterpriseDao.findById(id);
        return res.map(mapper::toInfoDto).orElse(null);
    }

    public Long applyEnterpriseCert(Long id, EnterpriseCertDto applyDto) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        String currentTime = now.format(formatter);

        List<EnterpriseCertStageEntity> certStageEntities = new ArrayList<>();
        certStageEntities.add(EnterpriseCertStageEntity.builder().message(CertStage.APPLY.getDesc()).status(ReviewStage.PASSED.getDesc())
                .time(currentTime).build());
        certStageEntities.add(EnterpriseCertStageEntity.builder().message(CertStage.PRE_REVIEW.getDesc()).status(ReviewStage.NOT.getDesc())
                .time("").build());
        certStageEntities.add(EnterpriseCertStageEntity.builder().message(CertStage.REVIEW.getDesc()).status(ReviewStage.NOT.getDesc())
                .time("").build());
        certStageEntities.add(EnterpriseCertStageEntity.builder().message(CertStage.FINISHED.getDesc()).status(ReviewStage.NOT.getDesc())
                .time("").build());

        EnterpriseCertEntity entity = mapper.toCertEntity(applyDto);
        entity.setEid(id);
        entity.setStatus(1);
        entity.setOnChain(false);

        entity.setProgress(certStageEntities);

        return certDao.save(entity).getEid();
    }

    public EnterpriseCertDto updateCertApply(Long id, EnterpriseCertDto applyDto) {

        EnterpriseCertEntity entity = mapper.toCertEntity(applyDto);
        entity.setEid(id);

        entity.setOnChain(false);

        EnterpriseCertEntity entityS = certDao.save(entity);
        return mapper.toCertDto(entityS);
    }

    public EnterpriseCertDto getCertApply(Long id) {
        Optional<EnterpriseCertEntity> res = certDao.findById(id);
        return res.map(mapper::toCertDto).orElse(null);
    }

    public CertApplyProgressDto updateCertApplyProgress(Long id, CertApplyProgressDto applyDto) {
        EnterpriseCertEntity entity = mapper.toCertEntity(applyDto);
        entity.setEid(id);
        entity = certDao.save(entity);
        return this.getCertApplyProgress(entity.getEid());
    }

    public CertApplyProgressDto getCertApplyProgress(Long id) {
        Optional<EnterpriseEntity> resOp = enterpriseDao.findById(id);
        if (resOp.isEmpty()){
            return null;
        }
        String companyName = resOp.get().getCompany();
        Optional<EnterpriseCertEntity> res = certDao.findById(id);
        if (res.isPresent()){
            CertApplyProgressDto applyProgress = mapper.toProgressDto(res.get());
            applyProgress.setCompany(companyName);
            return applyProgress;
        }
        return null;
    }

    @Override
    public List<CertApplyProgressDto> listCertApplyProgress() {
        List<EnterpriseCertEntity> entityList = certDao.findAll();
        List<CertApplyProgressDto> resList = new ArrayList<>();
        for (EnterpriseCertEntity cert:
                entityList) {
            resList.add(this.getCertApplyProgress(cert.getEid()));
        }
        return resList;
    }

    @Override
    public UploadInfo uploadToChainCert(Long id) {
        CertApplyProgressDto cert = this.getCertApplyProgress(id);
        if (null!=cert && 0 == cert.getStatus()){
            UploadInfo txInfo = blockchainService.uploadEnterpriseCert(id.toString(), cert.toString());
            cert.setOnChain(true);
            cert.setCommitTime(txInfo.getCommitTime());
            cert.setTransactionId(txInfo.getTransactionId());
            certDao.save(mapper.toCertEntity(cert));
            return txInfo;
        }
        throw new RuntimeException("cert not found or not reviewed");
    }
}
